#include "ControlObj.hpp"
#include "CameraDriver/Base/RealtimeCamera.hpp"
#include "CameraDriver/Base/StorageCamera.hpp"
#include "ControlCollege.hpp"
#include <fmt/format.h>
#include <functional>
#include <nlohmann/json.hpp>
#include <spdlog/spdlog.h>

void ControlObj::DoCameraCase(std::function<void()> realtime,
                              std::function<void()> storage) {
  switch (camera->GetCameraType()) {
  case BaseCamera::CameraType::REALTIME:
    realtime();
    break;
  case BaseCamera::CameraType::STORAGE:
    storage();
    break;
  default:
    throw std::logic_error("ControlObj Unknow camera type");
    break;
  }
}

ControlObj::ControlObj(ControlCollege *manager_, BaseDriver *driver_,
                       BaseCamera *camera_)
    : camera(camera_), driver(driver_), manager(manager_),
      UUID(camera_->GetUUID()) {
  camera->funcOffline = std::bind(&ControlObj::HandleOffline, this);
  camera->funcNewImage =
      std::bind(&ControlObj::HandleNewImage, this, std::placeholders::_1,
                std::placeholders::_2, std::placeholders::_3);
  camera->funcCameraMessage =
      std::bind(&ControlObj::HandleCameraMessage, this, std::placeholders::_1);
  SettingItemBuffer = camera->Config()->ItemList();
  mem = new ShareMem(UUID, camera->MaxSingleImgSize());
  record = new RecordFile();
}

ControlObj::~ControlObj() {
  delete mem;
  delete record;
}

std::tuple<std::string, std::string, long> ControlObj::GetShareMemInf() {
  return std::make_tuple<std::string, std::string, long>(
      mem->GetName().first, mem->GetName().second, camera->MaxSingleImgSize());
}

void ControlObj::StartShot() {
  if (ShotStatus)
    throw std::logic_error("ControlObj Shoting");
  DoCameraCase(
      [this]() {
        PreviewImgSize =
            dynamic_cast<RealtimeCamera *>(camera)->ShotImageType().size;
        dynamic_cast<RealtimeCamera *>(camera)->StartShot();
      },
      [this]() {
        PreviewImgSize =
            dynamic_cast<StorageCamera *>(camera)->PreviewImageType().size;
        dynamic_cast<StorageCamera *>(camera)->StartPreview();
      });
  ShotStatus = true;
}

void ControlObj::StopShot() {
  if (!ShotStatus)
    throw std::logic_error("ControlObj NotShot");
  DoCameraCase(
      [this]() { dynamic_cast<RealtimeCamera *>(camera)->StopShot(); },
      [this]() { dynamic_cast<StorageCamera *>(camera)->StopPreview(); });
  ShotStatus = false;
}

void ControlObj::StartRecord(BaseFormat *format,
                             const std::filesystem::path &path) {
  if (RecordStatus)
    throw std::logic_error("ControlObj Recording");

  BaseCamera::ImgInf info;
  // 获取信息
  DoCameraCase(
      [this, &info]() {
        info = dynamic_cast<RealtimeCamera *>(camera)->ShotImageType();
        RecordImgSize = info.size;
      },
      [this, &info]() {
        info = dynamic_cast<StorageCamera *>(camera)->RecordImageType();
        RecordImgSize = info.size;
      });
  // 测试是否正常
  try {
    record->Start(format, path,
                  {.imgWidth = (unsigned long)info.width,
                   .imgHeight = (unsigned long)info.height,
                   .pixelType = info.type,
                   .CameraUUID = UUID});
  } catch (const std::exception &e) {
    manager->funcMsg(e.what());
    throw;
  }
  // 执行动作
  DoCameraCase(
      []() {},
      [this]() { dynamic_cast<StorageCamera *>(camera)->StartRecord(); });
  RecordStatus = true;
}

void ControlObj::StopRecord() {
  if (!RecordStatus)
    throw std::logic_error("ControlObj NotRecording");
  DoCameraCase(
      []() {},
      [this]() { dynamic_cast<StorageCamera *>(camera)->StopRecord(); });
  record->Stop();
  RecordStatus = false;
}

BaseCamera::ImgInf ControlObj::GetRecordImageInf() {
  BaseCamera::ImgInf inf;
  DoCameraCase(
      [this, &inf]() {
        inf = dynamic_cast<RealtimeCamera *>(camera)->ShotImageType();
      },
      [this, &inf]() {
        inf = dynamic_cast<StorageCamera *>(camera)->RecordImageType();
      });
  return inf;
}

BaseCamera::ImgInf ControlObj::GetPreviewImageInf() {
  BaseCamera::ImgInf inf;
  DoCameraCase(
      [this, &inf]() {
        inf = dynamic_cast<RealtimeCamera *>(camera)->ShotImageType();
      },
      [this, &inf]() {
        inf = dynamic_cast<StorageCamera *>(camera)->PreviewImageType();
      });
  return inf;
}

void ControlObj::HandleNewImage(long frameID, char *data,
                                const std::string &additional) {
  try {
    DoCameraCase(
        [&]() {
          if (record->Status())
            *record << ImageType{frameID, RecordImgSize, (unsigned char *)data,
                                 additional};
          *mem = {frameID, PreviewImgSize, (unsigned char *)data};
        },
        [&]() {
          if (frameID == -1)
            *mem = {frameID, PreviewImgSize, (unsigned char *)data};
          else if (record->Status())
            *record << ImageType{frameID, RecordImgSize, (unsigned char *)data,
                                 additional};
        });
  } catch (const std::runtime_error &e) {
    spdlog::error(fmt::format("Camera {} : {}", UUID, e.what()));
    HandleCameraMessage(fmt::format("Camera {} : {}", UUID, e.what()));
  }
}

bool ControlObj::GetShotStatus() { return ShotStatus; }

bool ControlObj::GetRecordStatus() { return RecordStatus; }

void ControlObj::HandleOffline() { manager->HandleOffline(UUID); }

std::string ControlObj::GetSettingList() {
  nlohmann::json j = nlohmann::json::array();
  for (const auto &i : SettingItemBuffer) {
    nlohmann::json k = nlohmann::json::object();
    k["id"] = i.id;
    k["desc"] = nlohmann::json::parse(i.describe);
    k["type"] = (int)i.type;
    k["readonly"] = i.readonly;
    j.push_back(k);
  }
  return j.dump();
}

std::string ControlObj::GetSettingValue(long setid) {
  nlohmann::json j = nlohmann::json::object();
  switch (SettingItemBuffer.at(setid).type) {
  case BaseConfig::item_type::I_LONG:
  case BaseConfig::item_type::I_LIST: {
    long value;
    camera->Config()->Get(setid, value);
    j["value"] = value;
  } break;
  case BaseConfig::item_type::I_DOUBLE: {
    double value;
    camera->Config()->Get(setid, value);
    j["value"] = value;
  } break;
  case BaseConfig::item_type::I_STRING: {
    std::string value;
    camera->Config()->Get(setid, value);
    j["value"] = value;
  } break;
  case BaseConfig::item_type::I_BOOL: {
    bool value;
    camera->Config()->Get(setid, value);
    j["value"] = (nlohmann::json::boolean_t)value;
  } break;
  }
  j["type"] = (int)(SettingItemBuffer.at(setid).type);
  return j.dump();
}

bool ControlObj::SetSettingValue(const std::string &value) {
  nlohmann::json j = nlohmann::json::parse(value);
  switch ((BaseConfig::item_type)(int(j["type"]))) {
  case BaseConfig::item_type::I_LONG:
  case BaseConfig::item_type::I_LIST:
    return camera->Config()->Set((long)j["id"], j["value"].get<long>());
    break;
  case BaseConfig::item_type::I_DOUBLE:
    return camera->Config()->Set((long)j["id"], j["value"].get<double>());
    break;
  case BaseConfig::item_type::I_STRING:
    return camera->Config()->Set((long)j["id"], j["value"].get<std::string>());
    break;
  case BaseConfig::item_type::I_BOOL:
    return camera->Config()->Set((long)j["id"], j["value"].get<bool>());
    break;
  }
  return false;
}

void ControlObj::HandleCameraMessage(std::string inf) {
  manager->funcMsg(fmt::format("Camera {} : {}", UUID, inf));
}

float ControlObj::GetFPS() { return camera->FPS(); }
